using FluentParser.Expressions.Rules.Composition;
using FluentParser.Utils;
using FluentParser.Expressions.Rules.Decoration;

namespace FluentParser.Expressions
{
    /// <summary>
    /// A token is an entity which reads a text and return a value of
    /// type T.
    /// </summary>
    /// <typeparam name="TResult">Type of the resulting value</typeparam>
    public abstract class Expr<TResult> : BaseExpr<TResult>
    {
        /// <summary>
        /// Combine two expression using the choice rule
        /// </summary>
        /// <param name="left">The first expression</param>
        /// <param name="right">The second expression</param>
        /// <returns>The composed expression</returns>
        public static Expr<TResult> operator |(Expr<TResult> left, Expr<TResult> right)
        {
            return new ChoiceExpressionRule<TResult>(left, right);
        }

        /// <summary>
        /// Combine two expression using the greedy choice rule
        /// </summary>
        /// <param name="left">The first expression</param>
        /// <param name="right">The second expression</param>
        /// <returns>The composed expression</returns>
        public static Expr<TResult> operator &(Expr<TResult> left, Expr<TResult> right)
        {
            return new GreedyChoiceExpressionRule<TResult>(left, right);
        }

        /// <summary>
        /// Combine two expression that should be parsed back-to-back
        /// </summary>
        /// <param name="left">The first expression</param>
        /// <param name="right">The second expression</param>
        /// <returns>The composed expression</returns>
        public static Expr<Tuple<TResult, TResult>> operator +(Expr<TResult> left, Expr<TResult> right)
        {
            return left.Join(right);
        }

        /// <summary>
        /// Combine two expression that should be parsed back-to-back
        /// </summary>
        /// <param name="left">The first expression</param>
        /// <param name="right">The second expression</param>
        /// <returns>The composed expression</returns>
        public static Expr<TResult> operator +(Expr<TResult> left, Symbol right)
        {
            return left.Join(right);
        }

        /// <summary>
        /// Combine two expression that should be parsed back-to-back
        /// </summary>
        /// <param name="left">The first expression</param>
        /// <param name="right">The second expression</param>
        /// <returns>The composed expression</returns>
        public static Expr<TResult> operator +(Symbol left, Expr<TResult> right)
        {
            return left.Join(right);
        }
    }
}